﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using FinPlusCommon;
using log = Logger.Logger;

namespace FinPlusComponents
{
    public class FinPlusCashGap : FinPlusComponent
    {
        public override IFunctions Adaptor{ get{ return _underlyingInst.Functions(); } }
        public override string TestString { get { return Adaptor.ToString(); } }

        private readonly IFinPlusComp _conn;
        private readonly string _idField, _underlyingField, _startField, _endField, _valueField;
        private readonly Underlying _underlyingInst;
        private readonly DateTime _from;

        //construct
        public FinPlusCashGap(IFinPlusComp trades, string idField, string underlyingField, string startField, 
            string endField, string valueField, string from = null)
        {
            IsValid = true;
            _underlyingInst = new Underlying();
            _idField = idField;
            _underlyingField = underlyingField;
            _startField = startField;
            _endField = endField;
            _valueField = valueField;
            _from = from == null ? DateTime.Now.Date : DateTime.Parse(from);
            _conn = trades;
            Start();
        }

        //common control interface 
        public override void Dispose()
        {
            _conn.Dispose();
        }

        //private
        private void Start()
        {
            try
            {
                foreach (var func in _conn.Adaptor.Values)
                    Update(func);
                Calc();
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void Update(IFunction func)
        {
            try
            {
                var idField = func[_idField].ToString();
                var underlying = (string)func.Value(_underlyingField);
                var start = (DateTime)func.Value(_startField);
                var end = (DateTime)func.Value(_endField);
                var value = (double)func.Value(_valueField);
                
                _underlyingInst.Flush(underlying, idField);
                
                if(end > _from && !func.IsClosed)
                    _underlyingInst.Update(underlying, idField, _from, start < _from ? _from : start, end, value);
            }
            catch(Exception e)
            {
                log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void Calc()
        {
            try
            {
                _underlyingInst.Calc();
            }
            catch(Exception e)
            {
                log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        //event
        private void Query_Update(object s, EventArgs a)
        {
            try
            {
                if (s.GetType() == typeof(IFunction))
                    Update((IFunction)s);
            }
            catch(Exception e)
            {
                log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }
    }

    internal class Underlying : Dictionary<string, CashGaps>
    {
        private IFunctions _functions;
        private static int cashGapId;

        //construct
        public Underlying()
        {
            _functions = new Functions();
            cashGapId = 1;
        }

        //public
        public void Update(string underlying, string idField, DateTime current, DateTime from, DateTime end, double value)
        {
            if(!ContainsKey(underlying))
                this[underlying] = new CashGaps(underlying, cashGapId++, idField, current, from, end, value);
            else
                this[underlying].Update(idField, from, end, value);
        }
        
        public void Flush(string underlying, string idField)  
        {
            if (this.ContainsKey(underlying))
                this[underlying].Flush(idField);
        }

        public IFunctions Functions()
        {
            return _functions;
        }

        public void Calc() 
        {
            var user = Environment.UserName;
            foreach (var cashGap in this.Values)
            {
                var func = cashGap.Calc(user);
                _functions[func.Id] = func;
            }
        }
    }

    internal class CashGaps : SortedDictionary<DateTime, CashGap>//net notionals over datetime
    {
        public bool IsDirty {get; set;}
        public int Id { get; set; }
        public IFunction function { get; set; }

        private string _underlying;

        //construct
        public CashGaps(string underlying, int id, string idField, DateTime current, DateTime from, DateTime end, double value)
        {
            IsDirty = true;
            Id = id;
            function = new Function("CashGap");
            function.Id = Id;
            this[current] = new CashGap("-1", 0);//ensure chart starts from current date;
            this[from] = new CashGap(idField, value);
            this[end] = new CashGap(idField, -value);
            _underlying = underlying;
        }

        //public
        public void Update(string idField, DateTime from, DateTime end, double value)
        {
            //add start val to begining
            if(this.ContainsKey(from))
                this[from].Update(idField, value);
            else
                this[from] = new CashGap(idField, value);

            //reverse start val at end date
            if (this.ContainsKey(end))
                this[end].Update(idField, -value);
            else
                this[end] = new CashGap(idField, -value);
               
            
            IsDirty = true;
        }

        public void Flush(string idField)
        {
            foreach (var cashGap in this.Values)
                if (cashGap.ContainsKey(idField))
                {
                    cashGap.Remove(idField);
                    IsDirty = true;
                }
        }

        public IFunction Calc(string user)
        {
            if (!IsDirty) return function;
            
            //function = new Function(Id);
            function = new Function("CashGap", Id.ToString(), user, Id);
            function.IsDirty = true;
            
            function.TryAdd(new Item(4, Id, "Instrument", _underlying, "String", DateTime.Now, null));
            
            double total =0;
            int p = 5;
            foreach (var kvp in this)
            {
                total += kvp.Value.Total();
                function.TryAdd(new Item(p, Id, kvp.Key.ToString("dd-MMM-yyyy"), total.ToString(), "Double", kvp.Key, null));
                p++;
            }

            return function;
        }
    }

    internal class CashGap : Dictionary<string, double>//bond notional relating to repo id
    {
        //construct
        public CashGap(string idField, double value)
        {
            this[idField] = value;
        }

        //public
        public void Update(string idField, double value)
        {
            this[idField] = value;
        }

        public double Total()
        {
            double total = 0;
            foreach (var val in this.Values)
                total += val;

            return total;
        }
    }
   
}
